﻿using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage;
using RQX.Common.Core.Authority;
using RQX.Common.Core.Db.EF.Context;
using RQX.Common.Core.Db.EF.Entity;
using RQX.Common.Core.Db.EF.Entity.Interface;
using RQX.Common.Core.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace RQX.Common.Core.Db.EF.Repository
{
    public class BaseRepository<TEntity> : IRepository<TEntity>
        where TEntity : BaseEntity
    {
        public IRepository<TEntity> Repository;
        private DbSet<TEntity> _Repository;
        private IQueryable<TEntity> _AllSource;
        protected Microsoft.EntityFrameworkCore.DbContext _context;

        public BaseRepository() { }
        public BaseRepository(BaseDbContext context)
        {
            Init(context);
        }

        public BaseRepository<TEntity> Init(BaseDbContext context)
        {
            _Repository = context.Set<TEntity>();
            Repository = this;
            _context = context;
            if (typeof(IDelete).IsAssignableFrom(typeof(TEntity)))
            {
                _AllSource = _Repository.Where(k => ((IDelete)k).IsRemove == false).AsQueryable();
            }
            else
            {
                _AllSource = _Repository.AsQueryable();
            }
            return this;
        }


        public void Include(Expression<Func<TEntity, object>> expression, params Expression<Func<object, object>>[] thenIncludes)
        {
            var tmp = _AllSource.Include(expression);
            if (thenIncludes.Length != 0)
            {
                foreach (var thenInclude in thenIncludes)
                {
                    tmp = tmp.ThenInclude(thenInclude);
                }
            }
            _AllSource = tmp;
        }

        public IQueryable<TEntity> WhereIf(bool isRun, Expression<Func<TEntity, bool>> predicate)
        {
            if (isRun)
            {
                return _AllSource.Where(predicate);
            }
            else
            {
                return _AllSource;
            }
        }

        public IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> predicate)
        {
            return _AllSource.Where(predicate);
        }

        public IQueryable<TEntity> GetAll(bool isTracking = true)
        {
            if (isTracking)
            {
                return _AllSource;
            }
            else
            {
                return _AllSource.AsNoTracking();
            }
        }

        public TEntity Find(int primaryKey)
        {
            return GetAll().FirstOrDefault(k => k.Id == primaryKey);
        }
        public IEnumerable<TEntity> Find(params int[] primarykeyList)
        {
            return GetAll().Where(k => primarykeyList.Contains(k.Id));
        }
        [Obsolete]
        public List<TEntity> Find(List<int> primaryKeys)
        {
            return GetAll().Where(k => primaryKeys.Contains(k.Id)).ToList();
        }

        public IEnumerable<TEntity> Add(IEnumerable<TEntity> entities)
        {
            return DealAdd(entities);
        }

        public TEntity Add(TEntity entity)
        {
            return DealAdd(new List<TEntity>() { entity }).FirstOrDefault();
        }

        public IEnumerable<TEntity> Update(IEnumerable<TEntity> entities)
        {
            return DealUpdate(entities);
        }

        public TEntity Update(TEntity entity)
        {
            return DealUpdate(new[] { entity }).FirstOrDefault();
        }
        /// <summary>
        /// 条件更新
        /// </summary>
        /// <param name="entitys"></param>
        /// <param name="filterFunc"></param>
        /// <returns></returns>
        public IEnumerable<TEntity> Update(IEnumerable<TEntity> entitys, Func<IQueryable<TEntity>, IQueryable<TEntity>> filterFunc)
        {
            var repList = GetAll().Where(k => entitys.Select(m => m.Id).Contains(k.Id));
            var filterList = filterFunc(repList);
            var updatelist = entitys.Where(o => filterList.Select(k => k.Id).Contains(o.Id));
            return DealUpdate(updatelist);
        }

        public IEnumerable<TEntity> Remove(IEnumerable<TEntity> entities)
        {
            return DealRemove(entities);
        }
        public TEntity Remove(TEntity entity)
        {
            return DealRemove(new List<TEntity>() { entity }).FirstOrDefault();
        }

        public int Commit()
        {
            return _context.SaveChanges();
        }

        public IDbContextTransaction GetBeginTransaction()
        {
            return _context.Database.BeginTransaction();
        }

        //public void CommitTransaction()
        //{
        //    _context.Database.CommitTransaction();
        //}

        //public void RollBackTransaction()
        //{
        //    _context.Database.RollbackTransaction();
        //}


        /// <summary>
        /// 处理操作人事件
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        private IEnumerable<TEntity> DealAdd(IEnumerable<TEntity> entities)
        {
            if (typeof(ICreateUser).IsAssignableFrom(typeof(TEntity)))
            {
                var user = LoginUser.GetCurrentUser();
                entities.ForEach(item =>
                {
                    ((ICreateUser)item).CreateUserId = user.UserId;
                    ((ICreateUser)item).CreateUserName = user.UserName;
                    ((ICreateUser)item).CreateTime = DateTime.Now;
                });
            }
            if (typeof(IDelete).IsAssignableFrom(typeof(TEntity)))
            {
                entities.ForEach(item =>
                {
                    ((IDelete)item).CanDele = true;
                    ((IDelete)item).IsRemove = false;
                });
            }

            List<EntityEntry<TEntity>> resultList = new List<EntityEntry<TEntity>>();
            entities.ForEach(entity => resultList.Add(_Repository.Add(entity)));
            Commit();
            return resultList.Select(k => k.Entity).ToList();
        }

        /// <summary>
        /// 处理操作人事件
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="isDelete"></param>
        /// <returns></returns>
        private IEnumerable<TEntity> DealUpdate(IEnumerable<TEntity> entities, bool isDelete = false)
        {
            if (isDelete)
            {
                if (typeof(IDeleteUser).IsAssignableFrom(typeof(TEntity)))
                {
                    var user = LoginUser.GetCurrentUser();
                    entities.ForEach(item =>
                    {
                        ((IDeleteUser)item).DeleteUserId = user.UserId;
                        ((IDeleteUser)item).DeleteUserName = user.UserName;
                        ((IDeleteUser)item).DeleteTime = DateTime.Now;
                    });
                }
            }
            else
            {
                if (typeof(IUpdateUser).IsAssignableFrom(typeof(TEntity)))
                {
                    var user = LoginUser.GetCurrentUser();
                    entities.ForEach(item =>
                    {
                        ((IUpdateUser)item).UpdateUserId = user.UserId;
                        ((IUpdateUser)item).UpdateUserName = user.UserName;
                        ((IUpdateUser)item).UpdateTime = DateTime.Now;
                    });
                }
            }

            List<EntityEntry<TEntity>> resultList = new List<EntityEntry<TEntity>>();
            entities.ForEach(entity => resultList.Add(_Repository.Update(entity)));
            Commit();
            return resultList.Select(k => k.Entity);
        }

        /// <summary>
        /// 处理软删除事件
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        private IEnumerable<TEntity> DealRemove(IEnumerable<TEntity> entities)
        {
            if (typeof(IDelete).IsAssignableFrom(typeof(TEntity)))
            {
                if (!LoginUser.GetCurrentUser().IsAdmin)
                {
                    entities = entities.Where(item => ((IDelete)item).CanDele == true).ToList();
                }
                entities.ForEach(item => ((IDelete)item).IsRemove = true);
                return DealUpdate(entities, true);
            }
            else
            {
                List<EntityEntry<TEntity>> resultList = new List<EntityEntry<TEntity>>();
                entities.ForEach(entity => resultList.Add(_Repository.Remove(entity)));
                Commit();
                return resultList.Select(k => k.Entity);
            }
        }

    }
}
